package com.franklin.ideaplugin.easytesting.ui.execute;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.franklin.ideaplugin.api.ui.IBaseToolBarComponent;
import com.franklin.ideaplugin.api.utils.ProjectUtils;
import com.franklin.ideaplugin.api.utils.SwingUtils;
import com.franklin.ideaplugin.api.utils.spring.MySpringPropertyUtils;
import com.franklin.ideaplugin.easytesting.cache.IEasyTestingCache;
import com.franklin.ideaplugin.easytesting.cache.impl.PersistenceEasyTestingCache;
import com.franklin.ideaplugin.easytesting.entity.ExecuteResult;
import com.franklin.ideaplugin.easytesting.rpc.IExecuteResultHandler;
import com.franklin.ideaplugin.easytesting.rpc.dubbo.DubboConfig;
import com.franklin.ideaplugin.easytesting.rpc.dubbo.DubboRpcClient;
import com.franklin.ideaplugin.easytesting.rpc.entity.MethodInvokeData;
import com.franklin.ideaplugin.easytesting.rpc.entity.MethodParameter;
import com.franklin.ideaplugin.easytesting.rpc.entity.RegistryData;
import com.franklin.ideaplugin.easytesting.thread.EasyTestingThreadPool;
import com.franklin.ideaplugin.easytesting.utils.PsiUtils;
import com.google.common.collect.Maps;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.components.JBScrollPane;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Ye Junhui
 * @since 2023/8/29
 */
public class ExecuteDubboForm implements IBaseToolBarComponent {
    private JPanel mainPanel;
    private JPanel dubboConfigPanel;
    private JPanel methodParamParentPanel;
    private JScrollPane methodParamScroll;
    private JPanel methodParamPanel;
    private JTextField registryAddressTextField;
    private JTextField groupTextField;
    private JTextField versionTextField;
    private JPanel toolPanel;

    private final PsiMethod psiMethod;
    private final JButton executeBtn;
    private final List<PsiParameter> psiParameterList;
    private final JButton chooseRegistryBtn = new JButton();
    private final DubboConfig dubboConfig;
    private final Map<String, Object> historyParamMap;

    /**
     * 缓存填充值
     */
    private final Map<String, ParameterTextComponent> paramMap;
    private final Map<String, PsiParameter> psiParamMap;
    private Map<String, MethodParameter> executeParamMap = Maps.newLinkedHashMap();

    private BaseExecuteForm baseExecuteForm;
    private final IExecuteResultHandler executeResultHandler;

    public ExecuteDubboForm(
            PsiMethod psiMethod,
            JButton executeBtn,
            Map<String, Object> historyParamMap,
            IExecuteResultHandler<ExecuteResult> executeResultHandler
    ) {
        this.executeResultHandler = executeResultHandler;
        this.paramMap = Maps.newLinkedHashMap();
        this.psiParamMap = Maps.newLinkedHashMap();
        this.psiMethod = psiMethod;
        this.executeBtn = executeBtn;
        this.dubboConfig = IEasyTestingCache.getInstance(psiMethod.getProject()).getDubboConfigCache(psiMethod.getContainingClass());
        this.psiParameterList = PsiUtils.getPsiMethodParameters(psiMethod);
        this.historyParamMap = historyParamMap;
        this.baseExecuteForm = new BaseExecuteForm(
                psiMethod,
                methodParamPanel,
                psiParameterList,
                Lists.newArrayList(),
                historyParamMap,
                paramMap,
                psiParamMap,
                executeParamMap
        );

        init();
    }

    private void createUIComponents() {
        // TODO: place custom component creation code here
        this.methodParamScroll = new JBScrollPane();
    }

    private void init() {
        initParamPanel();
        initButton();
        initTextFiled();
    }

    private void initParamPanel(){
        int size = psiParameterList.size();
        if (size > 0) {
            methodParamPanel.setLayout(SwingUtils.createLayoutManager(size, 2));
        }
        this.baseExecuteForm.initMethodParamPanel();
    }

    private void initTextFiled() {
        this.registryAddressTextField.setText(this.dubboConfig.getRegistryAddress());
        this.groupTextField.setText(this.dubboConfig.getGroup());
        this.versionTextField.setText(this.dubboConfig.getVersion());
    }

    private void initButton() {
        initExecuteBtn();
        initChooseRegistryBtn();
    }

    private void initChooseRegistryBtn() {
        SwingUtils.clearActionListener(this.chooseRegistryBtn);
        this.chooseRegistryBtn.setIcon(AllIcons.General.Add);
        this.chooseRegistryBtn.setToolTipText("Choose dubbo registry from spring config file");
        this.chooseRegistryBtn.setText("Choose dubbo registry from spring config file");
        this.chooseRegistryBtn.addActionListener(e -> {
            Module[] modules = ModuleManager.getInstance(psiMethod.getProject()).getModules();
            List<String> registryList = Arrays.stream(modules)
                    .map(module -> MySpringPropertyUtils.getPropertyValue(module, "dubbo.registry.address"))
                    .filter(StrUtil::isNotBlank)
                    .distinct()
                    .collect(Collectors.toList());

            ListPopup listPopup = JBPopupFactory.getInstance().createListPopup(
                    new BaseListPopupStep<>(
                            "Choose the registry in spring config file",
                            registryList
                    ) {


                        @Override
                        public @NotNull String getTextFor(String value) {
                            return value;
                        }

                        @Override
                        public @Nullable PopupStep<?> onChosen(String selectedValue, boolean finalChoice) {
                            registryAddressTextField.setText(selectedValue);
                            return PopupStep.FINAL_CHOICE;
                        }


                    }
            );
            listPopup.showCenteredInCurrentWindow(ProjectUtils.getCurrProject());
        });

        //工具栏
        DefaultActionGroup actionGroup = new DefaultActionGroup();
        this.initToolBar(actionGroup);
        this.addToolButton(actionGroup,this.chooseRegistryBtn);
    }

    private void transferParam(){
        if (CollectionUtil.isNotEmpty(paramMap)) {
            executeParamMap = new LinkedHashMap<>();
            historyParamMap.clear();
            paramMap.forEach((k, textComponent) -> {
                String text = "";
                if (Objects.nonNull(textComponent)){
                    text = textComponent.getText();
                }
                PsiParameter psiParameter = psiParamMap.get(k);
                String typeStr = textComponent.getTypeStr(psiParameter);
                executeParamMap.put(k, new MethodParameter(typeStr, text));
            });
        }
    }

    private void initExecuteBtn() {
        SwingUtils.clearActionListener(this.executeBtn);
        this.executeBtn.addActionListener(e -> {
            this.transferParam();

            MethodInvokeData methodInvokeData = new MethodInvokeData();
            PersistenceEasyTestingCache easyTestingCache = (PersistenceEasyTestingCache) IEasyTestingCache.getInstance(ProjectUtils.getCurrProject());

            methodInvokeData.setClassQualifiedName(PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class).getQualifiedName());
            methodInvokeData.setMethodName(psiMethod.getName());
            methodInvokeData.setParameterMap(executeParamMap);
            long executeTime = System.currentTimeMillis();

            String executePath = easyTestingCache.getParamCachePath(easyTestingCache.getMethodKey(psiMethod)) + "/" + executeTime;

            methodInvokeData.setExecutePath(executePath);
            LinkedHashMap<String, Object> map = Maps.newLinkedHashMap();
            Map<String, MethodParameter> parameterMap = methodInvokeData.getParameterMap();
            parameterMap.forEach((k, v) -> {
                map.put(k, v.getValue());
            });


            ExecuteResult executeResult = new ExecuteResult(map, null, executeTime, false, executePath);
            easyTestingCache.cacheParamMap(psiMethod, executeResult);
            easyTestingCache.cacheExecuteScript(psiMethod, executeResult);

            //缓存dubbo配置
            String registry = StrUtil.blankToDefault(this.registryAddressTextField.getText(),"");
            String group = StrUtil.blankToDefault(this.groupTextField.getText(),null);
            String version = StrUtil.blankToDefault(this.versionTextField.getText(),null);
            this.dubboConfig.setRegistryAddress(registry);
            this.dubboConfig.setGroup(group);
            this.dubboConfig.setVersion(version);

            easyTestingCache.cacheDubboConfig(psiMethod.getContainingClass(),dubboConfig);

            EasyTestingThreadPool.getBizThreadPool().execute(() -> {
                ApplicationManager.getApplication().runReadAction(() -> {
                    try {
                        DubboRpcClient dubboRpcClient = new DubboRpcClient();
                        dubboRpcClient.invoke(
                                psiMethod,
                                executeResult,
                                methodInvokeData,
                                this.dubboConfig,
                                executeResultHandler
                        );
                    } catch (Exception exception) {
                        //ignore
                    }
                });
            });
        });
    }

    @Override
    public JComponent getMainPanel() {
        return this.mainPanel;
    }

    @Override
    public @NotNull JComponent getToolPanel() {
        return this.toolPanel;
    }
}
